package com.lhl.fw.db.repository;

import cn.hutool.core.convert.Convert;
import cn.hutool.db.PageResult;
import com.lhl.fw.db.dsl.PageListMapResult;
import com.lhl.fw.db.dsl.QueryDslContext;
import com.querydsl.core.Tuple;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.EntityPathBase;
import com.querydsl.core.types.dsl.PathBuilder;
import com.querydsl.jpa.JPQLQuery;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.Getter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport;
import org.springframework.data.jpa.repository.support.Querydsl;
import org.springframework.data.jpa.repository.support.QuerydslJpaPredicateExecutor;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.querydsl.SimpleEntityPathResolver;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * @className: BaseJpaRepositoryImpl
 * @description: 扩展自定义查询实现
 * @author: king
 * @date: 2020-09-03 15:28
 **/
public class BaseJpaRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
        implements BaseJpaRepository<T, ID> {

    @Getter
    protected final JPAQueryFactory jpaQueryFactory;
    @Getter
    protected final QuerydslJpaPredicateExecutor<T> jpaPredicateExecutor;
    @Getter
    protected final EntityManager entityManager;
    @Getter
    protected final Querydsl querydsl;
    @Getter
    private final EntityPath<T> path;

    protected BaseJpaRepositoryImpl(Class<T> domainClass, EntityManager em) {
        super(domainClass, em);
        this.entityManager = em;
        this.jpaPredicateExecutor =
                new QuerydslJpaPredicateExecutor<>(
                        JpaEntityInformationSupport.getEntityInformation(domainClass, em),
                        em,
                        SimpleEntityPathResolver.INSTANCE,
                        getRepositoryMethodMetadata());
        this.jpaQueryFactory = new JPAQueryFactory(em);
        this.path = SimpleEntityPathResolver.INSTANCE.createPath(domainClass);
        this.querydsl = new Querydsl(em, new PathBuilder<T>(path.getType(), path.getMetadata()));
    }

    @Override
    @Transactional(readOnly = true)
    public Page<T> findAll(Predicate predicate, Pageable pageable, OrderSpecifier<?>... orders) {
        final JPAQuery countQuery = jpaQueryFactory.selectFrom(path);
        countQuery.where(predicate);
        JPQLQuery<T> query = querydsl.applyPagination(pageable, countQuery);
        query.orderBy(orders);
        return PageableExecutionUtils.getPage(query.fetch(), pageable, countQuery::fetchCount);
    }

    @Override
    @Transactional(readOnly = true)
    public Page<T> queryDsl(Pageable pageable, List<Predicate> predicateList, EntityPathBase<T> entityPathBase, List<OrderSpecifier> orderSpecifierList) {
        List<T> list;
        //查询表
        JPAQuery<T> jpaQuery = jpaQueryFactory.selectFrom(entityPathBase);
        //查询条件
        if (predicateList != null && predicateList.size() > 0) {
            jpaQuery.where(predicateList.toArray(new Predicate[predicateList.size()]));
        }
        //排序方式
        if (orderSpecifierList != null && orderSpecifierList.size() > 0) {
            jpaQuery.orderBy(orderSpecifierList.toArray(new OrderSpecifier[orderSpecifierList.size()]));
        }
        //分页查询
        list = jpaQuery.offset(pageable.getOffset()).limit(pageable.getPageSize()).fetch();
        return new PageImpl<T>(list, pageable, jpaQuery.fetchCount());
    }


    @Override
    @Transactional(readOnly = true)
    public T queryOneDsl(Predicate predicate, EntityPathBase<T> entityPathBase) {
        return jpaQueryFactory.selectFrom(entityPathBase).where(predicate).fetchFirst();
    }

    @Override
    @Transactional(readOnly = true)
    public Page<Map<String, Object>> queryDslForPageListResult(QueryDslContext qdc, Pageable pageable) {
        JPAQuery<Tuple> jpaQuery = jpaQueryFactory.select(qdc.expressionToArray())
                .from(qdc.entityPathToArray())
                .where(qdc.predicatesToArray());
        List<Tuple> tuples = jpaQuery.orderBy(qdc.orderSpecifiersToArray())
                .offset(pageable.getOffset()).limit(pageable.getPageSize())
                .fetch();
        //返回结果
        List<Map<String, Object>> list = new LinkedList<>();
        //封装结果
        for (int i = 0; i < tuples.size(); i++) {
            //遍历tuples
            Map<String, Object> map = new LinkedHashMap<>();
            //一条信息
            for (Expression expression : qdc.getExpressions()) {
                //别名作为Key
                map.put(expression.toString().split(" as ")[1],
                        //获取结果
                        tuples.get(i).get(expression));
            }
            list.add(map);
        }
        return new PageImpl<Map<String, Object>>(list, pageable, jpaQuery.fetchCount());
    }
}